﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using DoNet.Common;
using DoNet.Util;

namespace AlibabaCloud.Framwork
{
    public class HttpHelper
    {
        public string AccessKeyId { get; private set; }
        public string AccessKeySecret { get; private set; }
        private const string Algorithm = "ACS3-HMAC-SHA256";
        private const string ContentType = "content-type";
        public HttpHelper(string accessKeyId, string accessKeySecret)
        {
            AccessKeyId = accessKeyId;
            AccessKeySecret = accessKeySecret;
        }

        private string CallApiAsync(Request request)
        {
            // 声明 httpClient
            using (var httpClient = new HttpClient())
            {
                // 构建 URL
                string url = $"{request.Protocol.ToLower()}://{request.Host}{request.CanonicalUri}";
                var uriBuilder = new UriBuilder(url);
                var query = new List<string>();

                // 添加请求参数
                foreach (var entry in request.QueryParam.OrderBy(e => e.Key.ToLower()))
                {
                    string value = entry.Value?.ToString() ?? "";
                    query.Add($"{entry.Key}={Uri.EscapeDataString(value)}");
                }

                uriBuilder.Query = string.Join("&", query);
                Console.WriteLine(uriBuilder.Uri);
                var requestMessage = new HttpRequestMessage
                {
                    Method = new HttpMethod(request.HttpMethod),
                    RequestUri = uriBuilder.Uri,
                };

                HttpMethodType httpMethodType = HttpMethodType.GET;
                switch (request.HttpMethod.ToUpper())
                {
                    case "GET":
                        httpMethodType = HttpMethodType.GET;
                        break;
                    case "POST":
                        httpMethodType = HttpMethodType.POST;
                        break;
                    case "PUT":
                        httpMethodType = HttpMethodType.PUT;
                        break;
                    case "DELETE":
                        httpMethodType = HttpMethodType.DELETE;
                        break;
                    default:
                        httpMethodType = HttpMethodType.GET;
                        break;
                }
                // 设置请求头
                SortedDictionary<string, object> headers = request.Headers;

                HttpContent content = null;
                if (request.Body != null)
                {
                    content = new ByteArrayContent(request.Body);
                    string contentType = request.Headers[ContentType].ToString();
                    content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
                }

                // 发送请求 读取响应内容
                return new HttpUtil().HttpclientCall(uriBuilder.Uri.AbsoluteUri, httpMethodType, content, headers);
            }
        }

        private string GetAuthorization(Request request)
        {
            // 处理queryParam中参数值为List、Map类型的参数，将参数平铺
            request.QueryParam = FlattenDictionary(request.QueryParam);

            // 步骤 1：拼接规范请求串
            StringBuilder canonicalQueryString = new StringBuilder();
            foreach (var entry in request.QueryParam.OrderBy(e => e.Key.ToLower()))
            {
                if (canonicalQueryString.Length > 0)
                {
                    canonicalQueryString.Append('&');
                }
                canonicalQueryString.Append($"{PercentCode(entry.Key)}={PercentCode(entry.Value?.ToString() ?? "")}");
            }

            byte[] requestPayload = request.Body == null ? Encoding.UTF8.GetBytes("") : request.Body;
            string hashedRequestPayload = Sha256Hash(requestPayload);
            request.Headers["x-acs-content-sha256"] = hashedRequestPayload;

            StringBuilder canonicalHeaders = new StringBuilder();
            StringBuilder signedHeadersSb = new StringBuilder();
            foreach (var entry in request.Headers.OrderBy(e => e.Key.ToLower()))
            {
                if (entry.Key.StartsWith("x-acs-", StringComparison.CurrentCultureIgnoreCase) || entry.Key.Equals("host", StringComparison.OrdinalIgnoreCase) || entry.Key.Equals(ContentType, StringComparison.OrdinalIgnoreCase))
                {
                    string lowerKey = entry.Key.ToLower();
                    string value = (entry.Value?.ToString() ?? "").Trim();
                    canonicalHeaders.Append($"{lowerKey}:{value}\n");
                    signedHeadersSb.Append($"{lowerKey};");
                }
            }
            string signedHeaders = signedHeadersSb.ToString().TrimEnd(';');
            string canonicalRequest = $"{request.HttpMethod}\n{request.CanonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedRequestPayload}";
            Console.WriteLine($"canonicalRequest:{canonicalRequest}");

            // 步骤 2：拼接待签名字符串
            string hashedCanonicalRequest = Sha256Hash(Encoding.UTF8.GetBytes(canonicalRequest));
            string stringToSign = $"{Algorithm}\n{hashedCanonicalRequest}";
            Console.WriteLine($"stringToSign:{stringToSign}");

            // 步骤 3：计算签名
            string signature = HmacSha256(AccessKeySecret, stringToSign);

            // 步骤 4：拼接 Authorization
            return $"{Algorithm} Credential={AccessKeyId},SignedHeaders={signedHeaders},Signature={signature}";
        }

        private string FormDataToString(Dictionary<string, object> formData)
        {
            Dictionary<string, object> tileMap = FlattenDictionary(formData);

            StringBuilder result = new StringBuilder();
            bool first = true;
            string symbol = "&";

            foreach (var entry in tileMap)
            {
                string value = entry.Value.ToString();
                if (!string.IsNullOrEmpty(value))
                {
                    if (!first)
                    {
                        result.Append(symbol);
                    }
                    first = false;
                    result.Append(PercentCode(entry.Key));
                    result.Append("=");
                    result.Append(PercentCode(value));
                }
            }
            return result.ToString();
        }

        private Dictionary<string, object> FlattenDictionary(Dictionary<string, object> dictionary, string prefix = "")
        {
            var result = new Dictionary<string, object>();
            if (dictionary != null)
            {
                foreach (var kvp in dictionary)
                {
                    string key = string.IsNullOrEmpty(prefix) ? kvp.Key : $"{prefix}.{kvp.Key}";

                    if (kvp.Value is Dictionary<string, object> nestedDict)
                    {
                        var nestedResult = FlattenDictionary(nestedDict, key);
                        foreach (var nestedKvp in nestedResult)
                        {
                            result[nestedKvp.Key] = nestedKvp.Value;
                        }
                    }
                    else if (kvp.Value is List<string> list)
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            result[$"{key}.{i + 1}"] = list[i];
                        }
                    }
                    else
                    {
                        result[key] = kvp.Value;
                    }
                }
            }
            return result;
        }

        private string HmacSha256(string key, string message)
        {
            using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
            {
                byte[] hashMessage = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
                return BitConverter.ToString(hashMessage).Replace("-", "").ToLower();
            }
        }

        private string Sha256Hash(byte[] input)
        {
            //.NET Framework 实现方法
            using (var sha256 = SHA256.Create())
            {
                byte[] hashBytes = sha256.ComputeHash(input);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            }

            //byte[] hashBytes = SHA256.HashData(input);
            //return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
        }

        private string PercentCode(string str)
        {
            //if (string.IsNullOrEmpty(str))
            //{
            //    throw new ArgumentException("输入字符串不可为null或空");
            //}
            return Uri.EscapeDataString(str).Replace("+", "%20").Replace("*", "%2A").Replace("%7E", "~");
        }

        /// <summary>
        /// RPC接口请求API接口
        /// </summary>
        /// <param name="action">API名称</param>
        /// <param name="product">产品 Id，如 云联络中心（CloudCallCenter）</param>
        /// <param name="queryParam">请求参数"in":"query"</param>
        /// <param name="formData">请求参数"in": "formData"</param>
        /// <param name="file">请求参数"in":"body"</param>
        /// <param name="httpMethod">请求方式，大部分RPC接口同时支持POST和GET，此处以POST为例</param>
        /// <param name="version">API版本号</param>
        /// <param name="region">API版本号</param>
        /// <returns></returns>
        public string CallApi(string action, string product, Dictionary<string, object> queryParam = null, Dictionary<string, object> formData = null, byte[] file = null, string httpMethod = "POST", string version = "2020-07-01", string region = "cn-shanghai")
        {
            string canonicalUri = "/"; // RPC接口无资源路径，故使用正斜杠（/）作为CanonicalURI
            string protocol = "HTTPS";
            /*
            服务接入点（Endpoint）
            服务接入点是访问阿里云服务的入口点，分公网接入地址和VPC接入地址。服务接入点通常是一个 URL，它指定了服务的访问协议、主机名、端口和路径等信息，客户端可以使用这些信息与服务进行通信。比如 https://ram.aliyuncs.com就是访问控制 RAM 对外提供的服务接入点。 
            
            公网接入地址 & VPC接入地址
            公网接入地址在全球都可访问。
            VPC接入地址只有在阿里云对应的地域内，且是VPC网络类型才可访问。
            VPC地址的好处：
            高安全性：VPC服务地址只能在VPC内部访问，提供更高的安全性和隐私性。
            更快的响应速度：由于VPC服务地址在VPC内部网络中运行，其响应速度通常比公网服务更快，且可以避免公网延迟和带宽限制等问题。
            更低的成本：VPC服务地址可以使用内部网络通信。

            部署类型&接入点域名
            一个云产品将服务部署在一个地域，称之为中心化部署；将服务部署在多个地域，则称之为区域化部署。不论是什么类型的部署，云服务都提供了Endpoint作为OpenAPI的入口。
            对于中心化部署，它的Endpoint为 <service code>.aliyuncs.com 的形式。以IMS为例，它的Endpoint是 ims.aliyuncs.com。
            对于区域化部署，它的Endpoint为 <service code>.<region id>.aliyuncs.com 的形式。以STS为例，它在杭州的Endpoint为 sts.cn-hangzhou.aliyuncs.com ，在北京的Endpoint为 sts.cn-beijing.aliyuncs.com。
            */
            // 云产品服务接入点 ${产品 Id}.${RegionId}.aliyuncs.com
            string host = $"ccc.cn-shanghai.aliyuncs.com";
            //string host = $"{GetUppercaseLetters(product)}.{region}.aliyuncs.com"; // 云产品服务接入点
            //string host = $"{GetUppercaseLetters(product)}.aliyuncs.com"; // 云产品服务接入点

            var request = new Request(httpMethod, canonicalUri, protocol, host, action, version);
            //https://localhost:44342/Ccc/sseApi?action=GetTurnServerList&product=CloudCallCenter&version=2020-07-01&region=cn-shanghai

            //请求参数"in":"query"
            if (queryParam != null)
            {
                // // RegionId在元数据中显示的类型是String，"in":"query"，必填
                // request.QueryParam["RegionId"] = "cn-hangzhou";
                // // InstanceId的在元数据中显示的类型是array，"in":"query"，非必填
                // List<string> instanceIds = new List<string>(){
                //     "i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX", "i-bp1incuofvzxXXXXXXXX"
                // };
                // request.QueryParam["InstanceId"] = instanceIds;
                foreach (var param in queryParam)
                {
                    request.QueryParam[param.Key] = param.Value;
                }
            }

            //请求参数"in": "formData"
            if (formData != null)
            {
                // // Context在元数据中显示的类型是String，"in":"query"，非必填
                // request.QueryParam["Context"] = "早上"; 
                // // FormatType、SourceLanguage、TargetLanguage等参数，在元数据中显示"in":"formData"
                // var body = new Dictionary<string, object>
                // {
                //     { "FormatType", "text" },
                //     { "SourceLanguage", "zh" },
                //     { "TargetLanguage", "en" },
                //     { "SourceText", "你好" },
                //     { "Scene", "general" },
                // };
                // var str = FormDataToString(body);
                // request.Body = Encoding.UTF8.GetBytes(str);
                request.Body = Encoding.UTF8.GetBytes(FormDataToString(formData));
                request.Headers[ContentType] = "application/x-www-form-urlencoded";
            }

            //请求参数"in":"body"
            if (file != null)
            {
                // // 请求参数在元数据中显示"in": "body"，通过body传参。
                // request.Body = File.ReadAllBytes(@"D:\test.png");
                // request.Headers["content-type"] = "application/octet-stream";
                request.Body = file;
                request.Headers[ContentType] = "application/octet-stream";
            }


            // DescribeInstanceStatus请求参数如下：

            // // RPC接口请求示例二：请求参数"in":"body"
            // string httpMethod = "POST"; 
            // string canonicalUri = "/"; 
            // string host = "ocr-api.cn-hangzhou.aliyuncs.com"; 
            // string xAcsAction = "RecognizeGeneral"; 
            // string xAcsVersion = "2021-07-07"; 
            // var request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);


            // // RPC接口请求示例三：请求参数"in": "formData"
            // string httpMethod = "POST"; 
            // string canonicalUri = "/"; 
            // string host = "mt.aliyuncs.com"; 
            // string xAcsAction = "TranslateGeneral"; 
            // string xAcsVersion = "2018-10-12"; 
            // var request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
            // // TranslateGeneral请求参数如下：
            // // Context在元数据中显示的类型是String，"in":"query"，非必填
            // request.QueryParam["Context"] = "早上"; 
            // // FormatType、SourceLanguage、TargetLanguage等参数，在元数据中显示"in":"formData"
            // var body = new Dictionary<string, object>
            // {
            //     { "FormatType", "text" },
            //     { "SourceLanguage", "zh" },
            //     { "TargetLanguage", "en" },
            //     { "SourceText", "你好" },
            //     { "Scene", "general" },
            // };
            // var str = FormDataToString(body);
            // request.Body = Encoding.UTF8.GetBytes(str);
            // request.Headers[ContentType] = "application/x-www-form-urlencoded";


            // // ROA接口POST请求
            // String httpMethod = "POST";
            // String canonicalUri = "/clusters";
            // String host = "cs.cn-beijing.aliyuncs.com";
            // String xAcsAction = "CreateCluster"; 
            // String xAcsVersion = "2015-12-15"; 
            // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
            // // 请求body，通过JsonConvert将body转成JSON字符串
            // var body = new SortedDictionary<string, object>
            // {
            //     { "name", "testDemo" },
            //     { "region_id", "cn-beijing" },
            //     { "cluster_type", "ExternalKubernetes" },
            //     { "vpcid", "vpc-2zeou1uod4ylaXXXXXXXX" },
            //     { "container_cidr", "10.0.0.0/8" },
            //     { "service_cidr", "172.16.1.0/20" },
            //     { "security_group_id", "sg-2ze1a0rlgeo7XXXXXXXX" },
            //     { "vswitch_ids", new List<string>{"vsw-2zei30dhfldu8XXXXXXXX"} },
            // };
            // string jsonBody = JsonConvert.SerializeObject(body, Formatting.None);
            // request.Body = Encoding.UTF8.GetBytes(jsonBody);
            // request.Headers[ContentType] = "application/json; charset=utf-8";

            // // ROA接口GET请求
            // String httpMethod = "GET";
            // // canonicalUri如果存在path参数，需要对path参数encode，percentCode({path参数})
            // String canonicalUri = "/clusters/" + PercentCode("c81d501a467594eab873edbf2XXXXXXXX") + "/resources";
            // String host = "cs.cn-beijing.aliyuncs.com"; 
            // String xAcsAction = "DescribeClusterResources"; 
            // String xAcsVersion = "2015-12-15"; 
            // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
            // request.QueryParam["with_addon_resources"]=true;

            // // ROA接口DELETE请求
            // String httpMethod = "DELETE";
            // // canonicalUri如果存在path参数，需要对path参数encode，percentCode({path参数})
            // String canonicalUri = "/clusters/" + PercentCode("c81d501a467594eab873edbf2XXXXXXXX");
            // String host = "cs.cn-beijing.aliyuncs.com"; 
            // String xAcsAction = "DeleteCluster"; 
            // String xAcsVersion = "2015-12-15"; 
            // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);

            request.Headers["Authorization"] = GetAuthorization(request);

            return CallApiAsync(request);
        }

        /// <summary>
        /// 获取字符串中大写字符，转成小写
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public string GetUppercaseLetters(string input)
        {
            return Regex.Matches(input, "[A-Z]").Cast<Match>().Aggregate(string.Empty, (current, match) => current + match.Value).ToLower();
        }
    }
}
